home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.unix
- From: autodesk!throop!kelvin@uunet.uu.net (John Walker)
- Subject: v25i150: settime - Set system time from atomic clock
- Sender: unix-sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: autodesk!throop!kelvin@uunet.uu.net (John Walker)
- Posting-Number: Volume 25, Issue 150
- Archive-Name: settime
-
- Whaddya expect from Switzerland? A time setting program for Unix!
-
- John Walker <autodesk!throop!kelvin@uunet.uu.net>
-
- NAME
- settime - Set system time from atomic clock
-
- SYNOPSIS
- settime [ -e ] [ -mmodem_port ] [ -ndial_string ] [ -ttimeout ]
-
- DESCRIPTION
- Don't you just hate it when your computer clock doesn't keep
- accurate time? Not only is a balky clock irritating, it can
- lead to more serious problems such as make failing to recom-
- pile files or files not being backed up, particularly in a
- local network environment.
-
- For a good time, call (303) 494-4774. It's from the govern-
- ment, and it's there to help you. The National Institute of
- Standards and Technology, NIST, (formerly the National
- Bureau of Standards) maintains the master time standard for
- the United States with a bank of cesium atomic clocks in
- Boulder, Colorado. They provide a dial-up service which
- furnishes a time and date directly traceable to the national
- time standard which, in turn, is based on the definition of
- the second. You don't get any more accurate than that. The
- service is free of charge (other than the regular long dis-
- tance telephone cost).
-
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # Makefile
- # settime.c
- # settime.1
- # settime.bat
- # nettime
- # This archive created: Mon Aug 26 13:05:52 1991
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'Makefile'" '(295 characters)'
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- cat << \SHAR_EOF > 'Makefile'
-
- RELFILES = Makefile settime.c settime.1 settime.bat nettime
-
- settime: settime.o
- cc settime.o -o settime
- strip settime
-
- clean:
- rm -f settime
- rm -f *.bak *.o
- rm -f core cscope.out
-
- manpage:
- nroff -man settime.1 | more
-
- lint:
- lint settime.c
-
- shar:
- shar -b -v $(RELFILES) >settime.shar
-
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'settime.c'" '(9771 characters)'
- if test -f 'settime.c'
- then
- echo shar: will not over-write existing file "'settime.c'"
- else
- cat << \SHAR_EOF > 'settime.c'
- /*
-
- Set UNIX system clock from the atomic clock at the National
- Institute of Standards and Technology (NIST) in Boulder Colorado.
-
- Designed and implemented in May of 1991 by John Walker.
-
- This program, which should be run when in super-user mode, dials
- into the telephone time service maintained by the NIST in Boulder,
- obtains the time from the atomic clock there (obtaining two
- consecutive valid readings to guard against communication errors),
- then sets the system clock at the moment the time hack is received
- for the next second. While waiting for the tick, the program goes
- into a high-priority CPU loop waiting for the tick to be received
- to obtain the best accuracy.
-
- The modem port, modem dial codes and telephone number, and various
- other parameters are set on the command line. The defaults are
- equivalent to the following:
-
- settime
- -M/dev/cua0 -- Modem port
- -NATDT13034944774 -- Dial command and phone number
- -T120 -- Two minute timeout
-
- If you specify the "-E" option, all modem input and output will be
- echoed to standard output.
-
- You don't have to be super-user to run the progrmam, but if you
- aren't the system time and date won't be changed, as only the
- super-user may change them.
-
- */
-
- #define REVDATE "13th May 1991"
-
- #include <sys/types.h>
- #include <sys/time.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <termio.h>
- #include <time.h>
-
- #define FALSE 0
- #define TRUE 1
-
- #define EOS '\0'
-
- struct termio newmode, oldmode;
-
- static char *modem = "/dev/cua0"; /* Modem file name */
- static char *dialstring = "ATDT13034944774"; /* Dial command */
- static int echoin = FALSE; /* Echo modem info if nonzero */
- static int port; /* Modem file descriptor */
- static char line[132]; /* Input buffer */
- static int jdate1970 = 2440588; /* Julian date of January 1, 1970 */
- static long usdelay; /* Line delay in microseconds */
- static time_t bailout; /* Time to give up if no success */
-
- /* TIMEDOUT -- Check for timeout expired. */
-
- static int timedout()
- {
- return time(NULL) > bailout;
- }
-
- /* MREAD -- Read next character from modem. */
-
- static int mread(buf)
- char *buf;
- {
- return read(port, buf, 1);
- }
-
-
- /* INLINE -- Obtain a newline terminated input line from the modem. */
-
- static int inline()
- {
- int l = 0;
- char buf;
-
- while (TRUE) {
- if (mread(&buf) > 0) {
- if (echoin) {
- putchar(buf);
- }
- if (buf == '\n') {
- line[l] = EOS;
- break;
- }
- if (buf >= ' ' && l < 131) {
- line[l++] = buf;
- }
- } else {
- if (timedout()) {
- strcpy(line, "BUSY");
- return FALSE;
- }
- usleep(4000L);
- }
- }
- return TRUE;
- }
-
- /* JDATE -- Convert internal GMT date and time to Julian day
- and fraction. */
-
- static long jdate(t)
- struct tm *t;
- {
- long c, m, y;
-
- y = t->tm_year + 1900;
- m = t->tm_mon + 1;
- if (m > 2)
- m = m - 3;
- else {
- m = m + 9;
- y--;
- }
- c = y / 100L; /* Compute century */
- y -= 100L * c;
- return t->tm_mday + (c * 146097L) / 4 + (y * 1461L) / 4 +
- (m * 153L + 2) / 5 + 1721119L;
- }
-
- /* PARSE_NIST -- Parse NIST time string into a "tm" structure. */
-
- static int parse_nist()
- {
- int jd, yr, mo, da, hh, mm, ss, dst, ls, msadvw, msadvf;
- char dut1[3], tname[20];
- time_t gt;
- struct tm t;
-
- if (sscanf(line, "%5d %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3c %3d.%1d %s",
- &jd, &yr, &mo, &da, &hh, &mm, &ss, &dst, &ls, dut1, &msadvw,
- &msadvf, tname) < 13)
- return 0;
-
- if (strncmp(tname, "UTC(NIST)", 9) != 0)
- return 0;
-
- t.tm_sec = ss;
- t.tm_min = mm;
- t.tm_hour = hh;
- t.tm_mday = da;
- t.tm_mon = mo - 1;
- t.tm_year = (yr > 90) ? yr : (yr + 100);
- t.tm_isdst = (dst > 0) && (dst <= 50);
- t.tm_wday = ((jd + 3) % 7);
-
- usdelay = ((msadvw * 10) + msadvf) * 100;
-
- gt = ((((jdate(&t) - jdate1970) * 24 + t.tm_hour) * 60) +
- t.tm_min) * 60 + t.tm_sec;
-
- #ifdef DEBUG
- { char *cp = asctime(gmtime(>));
- printf("Clock %d (%d) Date and time: %s Delay = %d\n", gt, time(NULL), cp,
- usdelay);
- }
- #endif
- return gt;
- }
-
- int main(argc, argv)
- int argc; char *argv[];
- {
- int i, success;
- char *cp, opt;
- time_t ptime, ntime;
- struct timeval tv;
- int timeout = 120; /* How long to attempt to get time */
-
- #ifdef TEST1
- strcpy(line, "48388 91-05-12 20:48:58 50 0 +.3 045.0 UTC(NIST) *");
- parse_nist(&ntime);
- return 0;
- #endif
-
- for (i = 1; i < argc; i++) {
- cp = argv[i];
- if (*cp == '-') {
- opt = *(++cp);
- if (islower(opt))
- opt = toupper(opt);
- switch (opt) {
-
- case 'E':
- echoin = TRUE;
- break;
-
- case 'M':
- modem = cp + 1;
- break;
-
- case 'N':
- dialstring = cp + 1;
- break;
-
- case 'T':
- timeout = atoi(cp + 1);
- break;
-
- case '?':
- case 'U':
- fprintf(stderr,"\nSETTIME -- Set time from NIST time service. Call");
- fprintf(stderr,
- "\n with settime [options]");
- fprintf(stderr,"\n");
- fprintf(stderr,"\n Options:");
- fprintf(stderr,"\n -E Echo modem input and output");
- fprintf(stderr,"\n -Mdev Use modem device dev");
- fprintf(stderr,"\n -Ncmd Use cmd as modem dial command");
- fprintf(stderr,"\n -Tn Set timeout to n seconds");
- fprintf(stderr,"\n");
- fprintf(stderr,"\n (P) Throoput Ltd. %s", REVDATE);
- fprintf(stderr,"\n All Rights Reversed");
- fprintf(stderr,"\n");
- return 0;
- }
- } else {
- }
- }
-
- if ((port = open(modem, O_RDWR | O_NDELAY)) == -1) {
- fprintf(stderr, "Cannot open modem port %s\n", modem);
- return 2;
- }
- if (ioctl(port, TCGETA, &oldmode) == -1) { /* Get current tty mode */
- perror(modem);
- return 2;
- }
- newmode = oldmode; /* Save it to restore port later */
- bailout = time(NULL) + timeout; /* Set time to give up */
-
- if (echoin) {
- setbuf(stdout, NULL); /* Make echoed output unbuffered */
- }
-
- /* Set the modem port to the proper modes for calling the time
- service. */
-
- newmode.c_iflag = IGNBRK | IGNPAR | ISTRIP;
- newmode.c_oflag = 0;
- newmode.c_cflag = B1200 | CS7 | CREAD | PARENB | HUPCL;
- newmode.c_lflag = 0;
- if (ioctl(port, TCSETA, &newmode) == -1) {
- fprintf(stderr, "Cannot set modem to desired modes\n");
- perror(modem);
- return 2;
- }
-
- /* Dial the time service. */
-
- if (fcntl(port, F_SETFL, FNDELAY) == -1) {
- fprintf(stderr, "Cannot set port to non-delayed response\n");
- perror(modem);
- return 2;
- }
- if (write(port, "\r\r\r", 3) != 3) {
- fprintf(stderr, "Error sending CR's to modem\n");
- perror(modem);
- return 2;
- }
- sleep(1);
- while (read(port, &opt, 1) > 0) ; /* Flush input */
- if (echoin) {
- printf("%s\n", dialstring);
- }
- if (write(port, dialstring, strlen(dialstring)) != strlen(dialstring) ||
- write(port, "\r", 1) != 1) {
- fprintf(stderr, "Error sending dial string to modem\n");
- perror(modem);
- }
-
- /* Wait until we're connected. If we get a busy signal, give up
- and report an error status. */
-
- while (inline()) {
- if ((strncmp(line, "CONNECT", 7) == 0) || (line[0] == '5')) {
- break;
- }
- if (strncmp(line, "BUSY", 4) == 0 ||
- strncmp(line, "NO ", 3) == 0 ||
- strncmp(line, "ERROR", 5) == 0 ||
- line[0] == '3' || line[0] == '4' || line[0] == '6' ||
- line[0] == '7' || line[0] == '8') {
- fprintf(stderr, "Unable to connect.\n");
- return 1;
- }
- }
-
- /* At this point we're connected. Ignore all data from the line
- until we receive two consecutive valid time signals one second
- apart. Once we've received these two signals, prepare the
- time setting for the next second and spin until we receive
- the "*" that indicates the correct instant to set the clock.
- At that moment, change the system clock. */
-
- ptime = 0;
- success = FALSE;
- while (!success && !timedout() && inline()) {
- if (ptime == 0) {
- ptime = parse_nist();
- ntime = 0;
- } else if (ntime == 0) {
- ntime = parse_nist();
- if (ntime == (ptime + 1)) {
- int rpyet = 0;
-
- tv.tv_sec = ntime + 1;
- tv.tv_usec = 0;
- while (TRUE) {
- char buf;
- int ticker = 0; /* CPU loop timeout check */
-
- if (mread(&buf) > 0) {
- if (echoin) {
- putchar(buf);
- }
- if ((buf == '*') || (buf == '#')) {
- settimeofday(&tv, NULL);
- success = TRUE;
- nice(10); /* Drop to normal priority */
- break;
- } else if (buf == ')') {
- rpyet = 1;
- nice(-10); /* Set high priority */
- } else if (buf < ' ') {
- ptime = 0;
- if (rpyet) {
- nice(10); /* Drop to normal priority */
- }
- }
- } else {
- if (!rpyet) {
- usleep(2000L);
- if (timedout()) {
- break;
- }
- } else {
- if (((++ticker) % 100) == 0) {
- if (timedout()) {
- break;
- }
- }
- }
- }
- }
- } else {
- ptime = 0;
- }
- }
- }
-
- if (echoin) {
- putchar('\n');
- }
- if (success) {
- printf("Universal time: %s", asctime(gmtime(&tv.tv_sec)));
- printf("Local time: %s", asctime(localtime(&tv.tv_sec)));
- }
-
- /* Restore port to original modes and close, disconnecting the
- modem. */
-
- ioctl(port, TCSETAW, &oldmode);
- ioctl(port, TCFLSH, 2);
- close(port);
-
- return 0;
- }
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'settime.1'" '(9057 characters)'
- if test -f 'settime.1'
- then
- echo shar: will not over-write existing file "'settime.1'"
- else
- cat << \SHAR_EOF > 'settime.1'
- .TH SETTIME 1 "13 MAY 1991"
- .UC 4
- .SH NAME
- settime \- Set system time from atomic clock
- .SH SYNOPSIS
- .B settime
- [
- .B \-e
- ] [
- .BI \-m modem_port
- ] [
- .BI \-n dial_string
- ] [
- .BI \-t timeout
- ]
- .SH DESCRIPTION
- Don't you just
- .I hate
- it when your computer clock doesn't keep accurate time? Not only is
- a balky clock irritating, it can lead to more serious problems such as
- .B make
- failing to recompile files or files not being backed up, particularly
- in a local network environment.
- .PP
- For a good time, call (303) 494-4774. It's from the government, and
- it's there to help you. The National Institute of Standards and
- Technology, NIST, (formerly the National Bureau of Standards)
- maintains the master time standard for the United States with a bank
- of cesium atomic clocks in Boulder, Colorado. They provide a dial-up
- service which furnishes a time and date directly traceable to the
- national time standard which, in turn, is based on the definition of
- the second. You don't get any more accurate than that. The service is
- free of charge (other than the regular long distance telephone cost).
- .PP
- The time and date are provided at 1200 baud, with compensation for
- line and modem delays. By carefully adjusting for speed of light
- lag, modem character serialisation and assembly time, and computer
- response time, it is usually possible to obtain the time to within two
- milliseconds of the correct value. In practice, it is rarely worth
- going to all the added difficulty since most computer clocks drift
- sufficiently to erase such a precision setting within a couple of
- hours. A very simple time setting procedure suffices to obtain the
- time to within 10 or 20 milliseconds of the standard, which is usually
- more than adequate.
- .PP
- .I settime
- is a Unix program that dials the NIST time service with a Hayes-like
- modem, obtains the time from the standard, performing validity
- checking to guard against communication errors, then, if run by the
- super-user, sets the computer's time of day clock with the
- .B settimeofday(\|)
- call.
-
- .SH OPTIONS
- .TP 10
- .B \-e
- Echo modem input and output to standard output. This option is
- intended for debugging only; using it introduces delays which reduce
- the accuracy with with the system clock is set.
- .TE
- .TP
- .BI \-m modem_port
- Use
- .I modem_port
- to dial the NIST time service. If omitted, the default port of
- .B /dev/cua0
- is used.
- .TP
- .BI \-n dial_string
- The given
- .I dial_string
- is sent to the modem to dial the NIST time service. The string
- may contain all required modem commands in addition to the telephone
- number. A carriage return is automatically appended to the given
- string. If the option is omitted, a default dial string of
- .B ATDT13034944774
- is used. If you require pulse dialing, a prefix to get an outside
- line, or special modem setup, specify an alternative dial string with
- the required modes.
- .TP
- .BI \-t timeout
- .I settime
- allows two minutes to obtain the time from NIST; this usually suffices
- and the time limit prevents running up large phone bills if NIST is
- out of service. You can change the timeout, if desired, by specifying
- the length in seconds, with this option.
-
- .SH BUGS
- The program has currently been tested only on a Sun SPARCStation 2
- with a Hayes Smartmodem 2400. Although I've tried to use only the
- simplest and most portable facilities in this program, data
- communications always involves a modicum of pain when porting to a new
- system, and this program will probably prove no exception. Please
- send me any portability or other fixes you make for your system.
- .PP
- The program could be even more careful than at present in verifying
- correctness of the received time and date and in checking for odd
- communications failures. So far, the level of checking in the program
- has proven adequate.
- .PP
- If run by a user other than the super-user, no warning is given that
- the system time is not changed.
- .PP
- The ultra-precise mode in which the actual modem and line delay is measured
- by the NIST computer and compensated for is not implemented. This
- mode requires instantaneous local echo-back of received characters.
- Unix systems cannot, in my experience,
- provide the consistent real-time response required by this mode.
- .SH SEE ALSO
- .BR settimeofday (2)
- .SH AUTHOR
- John Walker
- .br
- Autodesk SA
- .br
- Avenue des Champs-Montants 14b
- .br
- CH-2074 MARIN
- .br
- Switzerland
- .br
- Fax: 038/33 88 15
- .br
- Voice: 038/33 76 33
- .br
- Usenet: kelvin@Autodesk.com
- .SH DETAILS OF THE TIME SERVICE
-
- .ce 2
- DESCRIPTION OF THE
- AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
-
-
- The following is transmitted (at 1200 Baud) after completion of the
- telephone connection.
-
- .nf
- ? = HELP
- National Institute of Standards and Technology
- Telephone Time Service
-
- D L D
- MJD YR MO DA H M S ST S UT1 msADV <OTM>
- 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
- 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
- 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
- 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
- 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
- 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
- etc..etc...etc.......
- .fi
-
- UTC = Universal Time Coordinated, the official world time referred to the
- zero meridian.
- .PP
- DST = Daylight savings time characters, valid for the continental U.S., are
- set as follows:
-
- .nf
- 00 = We are on standard time (ST).
- 50 = We are on DST.
- 99 to 51 = Now on ST, go to DST when your local time is
- 2:00 am and the count is 51. The count is
- decremented daily at 00 (UTC).
- 49 to 01 = Now on DST, go to ST when your local time is
- 2:00 am and the count is 01. The count is
- decremented daily at 00 (UTC).
- .fi
- .PP
- The two DST characters provide up to 48 days advance notice of a change in
- time. The count remains at 00 or 50 at other times.
-
- .PP
-
- LS = Leap second flag is set to "1" to indicate that a leap second is to be
- added as 23:59:60 (UTC) on the last day of the current UTC month. The LS
- flag will be reset to "0" starting with 23:59:60 (UTC). The flag will
- remain on for the entire month before the second is added. Leap seconds
- are added as needed at the end of any month. Usually June and/or December
- are chosen.
-
- .PP
-
-
- DUT1 = Approximate difference between earth rotation time (UT1) and UTC, in
- steps of 0.1 second. DUT1 = UT1 - UTC
-
- .PP
-
- MJD = Modified Julian Date, often used to tag certain scientific data.
-
- .PP
-
- The full time format is sent at 1200 Baud, 8 bit, 1 stop, no parity.
- The format at 300 Baud is also 8 bit, 1 stop, no parity.
- At 300 Baud the MJD and DUT1 values are deleted and the
- time is transmitted only on even seconds.
-
- .PP
-
- Maximum on line time will be 56 seconds. If all lines are busy at any time,
- the oldest call will be terminated if it has been on line more than 28
- seconds, else, the call that first reaches 28 seconds will be terminated.
-
- .PP
-
- Current time is valid at the "on-time" marker (OTM), either "*" or "#".
- The nominal on-time marker (*) will be transmitted 45 ms early to account
- for the 8 ms required to send 1 character at 1200 Baud, plus an additional
- 7 ms for delay from NIST to the user, and approximately 30 ms "scrambler"
- delay inherent in 1200 Baud modems. If the caller echoes all characters,
- NIST will measure the round trip delay and advance the on-time marker so
- that the midpoint of the stop bit arrives at the user on time. The amount
- of msADV will reflect the actual required advance in milliseconds and the
- OTM will be a "#". The NIST system requires 4 or 5 consecutive delay
- measurements which are consistent before switching from "*" to "#".
- If the user has a 1200 Baud modem with the same internal delay as that used
- by NIST, then the "#" OTM should arrive at the user within +-2 ms of the
- correct time. However, NIST has studied different brands of 1200 Baud
- modems and found internal delays from 24 ms to 40 ms and offsets of the
- "#" OTM of +-10 ms. For many computer users, +-10 ms accuracy should be
- more than adequate since many computer internal clocks can only be set with
- granularity of 20 to 50 ms. In any case, the repeatability of the offset
- for the "#" OTM should be within +-2 ms, if the dial-up path is reciprocal
- and the user doesn't change the brand or model of modem used. This should
- be true even if the dial-up path on one day is a land-line of less than
- 40 ms (one way) and on the next day is a satellite link of 260 to 300 ms.
- In the rare event that the path is one way by satellite and the other way
- by land line with a round trip measurement in the range of 90 to 260 ms,
- the OTM will remain a "*" indicating 45 ms advance.
-
- .PP
-
- For user comments write:
-
- .nf
- NIST-ACTS
- Time and Frequency Division
- Mail Stop 524
- 325 Broadway
- Boulder, CO 80303
- USA
- .fi
-
- Software for setting (PC)DOS compatable machines is available
- on a 360-kbyte diskette for $35.00 from:
- .PP
- NIST Office of Standard Reference Materials
- B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, USA, (301) 975-6776
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'settime.bat'" '(409 characters)'
- if test -f 'settime.bat'
- then
- echo shar: will not over-write existing file "'settime.bat'"
- else
- cat << \SHAR_EOF > 'settime.bat'
- rem
- rem Set the DOS time, date, and time zone from a master
- rem time kept on a Unix system via NFS.
- rem
- rem Usage:
- rem settime time_server_host
- rem
- rsh %1 date +\"time %%T\" >zztime.bat
- rsh %1 date +\"date %%D\" >>zztime.bat
- rsh %1 "date|cut -d' ' -f5-|awk '{ ORS=\"\";print \"set tz=\" $1;if(NF>2) print \" \" $2;print \"\n\"}'">>zztime.bat
- call zztime.bat
- del zztime.bat
- SHAR_EOF
- chmod +x 'settime.bat'
- fi # end of overwriting check
- echo shar: extracting "'nettime'" '(257 characters)'
- if test -f 'nettime'
- then
- echo shar: will not over-write existing file "'nettime'"
- else
- cat << \SHAR_EOF > 'nettime'
- #! /bin/sh
- #
- # Set time from network time server machine given as
- # first argument. You must be the super-user for the
- # time to be set.
- #
- # Example:
- # nettime worldbrain
- #
- rsh $1 date +\"date %y%m%d%H%M.%S\" | sh
- SHAR_EOF
- chmod +x 'nettime'
- fi # end of overwriting check
- # End of shell archive
- exit 0
- ----------------------------- End source ---------------------- settime.shar
-
-
-